/**
 * @file st7789.c
 * @author cangyu (sky.kirto@qq.com)
 * @brief
 * @version 0.1
 * @date 2024-09-02
 *
 * @copyright Copyright (c) 2024, CorAL. All rights reserved.
 *
 */

/* ==================== [Includes] ========================================== */

#include "../lcd_drive_internal.h"

/* ==================== [Defines] =========================================== */

#define ST7789_RESOLUTION_HOR 240
#define ST7789_RESOLUTION_VER 320

#define TAG "st7789"

/* ==================== [Typedefs] ========================================== */

typedef struct _st7789_init_t {
    uint32_t cmd;
    uint8_t  data[14];
    uint32_t len;
} st7789_init_t;


/* ==================== [Static Prototypes] ================================= */

static xf_err_t st7789_init(lcd_t self);
static xf_err_t st7789_draw_bitmap(lcd_t self, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *bitmap);
static xf_err_t st7789_set_window(lcd_t self, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);

/* ==================== [Static Variables] ================================== */

const static lcd_drive_out_t st7789_out = {
    .res_hor = ST7789_RESOLUTION_HOR,
    .res_ver = ST7789_RESOLUTION_VER,
    .init = st7789_init,
    .draw_bitmap = st7789_draw_bitmap
};

/* ==================== [Macros] ============================================ */

/* ==================== [Global Functions] ================================== */

void lcd_st7789_vfunc_register(void)
{
    lcd_controller_driver_reg(SCR_CONTROLLER_st7789, &st7789_out);
}

/* ==================== [Static Functions] ================================== */

static xf_err_t st7789_init(lcd_t self)
{
    const static st7789_init_t init[] = {
        {0x3A, {0x05}, 1},
        {0xB2, {0x0C, 0x0C, 0x00, 0x33, 0x33}, 5},
        {0xB7, {0x35}, 1},
        {0xBB, {0x19}, 1},
        {0xC0, {0x2C}, 1},
        {0xC2, {0x01}, 1},
        {0xC3, {0x12}, 1},
        {0xC4, {0x20}, 1},
        {0xC6, {0x0F}, 1},
        {0xD0, {0xA4, 0xA1}, 2},
        {0xE0, {0xD0, 0x04, 0x0D, 0x11, 0x13, 0x2B, 0x3F, 0x54, 0x4C, 0x18, 0x0D, 0x0B, 0x1F, 0x23}, 14},
        {0xE1, {0xD0, 0x04, 0x0C, 0x11, 0x13, 0x2C, 0x3F, 0x44, 0x51, 0x2F, 0x1F, 0x1F, 0x20, 0x23}, 14},
        {0x21, {0}, 0},
        {0x11, {0}, 0},
        {0x29, {0}, 0},
    };
    xf_err_t ret = XF_OK;

    XF_LOGI(TAG, "st7789 init");

    ret |= LCD_RESET(self);
    for (int i = 0; i < sizeof(init) / sizeof(init[0]); i++) {
        ret |= LCD_WRITE_CMD(self, &init[i].cmd, 1);
        if (init[i].len != 0)
        {
            ret |= LCD_WRITE_DATA(self, &init[i].data, init[i].len);
        }
    }
    ret |= LCD_BACKLIGHT(self, 1);

    XF_LOGI(TAG, "st7789 init ret:%d", ret);

    return ret;
}

static xf_err_t st7789_draw_bitmap(lcd_t self, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *bitmap)
{
    xf_err_t ret = XF_OK;

    ret |= st7789_set_window(self, x, y, x + w - 1, y + h - 1);
    uint32_t len = w * h;
    ret |= LCD_WRITE_DATA(self, bitmap, len*2);
    return 0;
}


static xf_err_t st7789_set_window(lcd_t self, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
    xf_err_t err = XF_OK;
    lcd_drive_offset(self, &x0, &y0, &x1, &y1);
    st7789_init_t init[] = {
        {0x2A, {0}, 4},
        {0x2B, {0}, 4},
        {0x2C, {0}, 0},
    };
    init[0].data[0] = (x0 >> 8) & 0xff;
    init[0].data[1] = x0 & 0xff;
    init[0].data[2] = (x1 >> 8) & 0xff;
    init[0].data[3] = x1 & 0xff;
    init[1].data[0] = (y0 >> 8) & 0xff;
    init[1].data[1] = y0 & 0xff;
    init[1].data[2] = (y1 >> 8) & 0xff;
    init[1].data[3] = y1 & 0xff;

    for (int i = 0; i < sizeof(init) / sizeof(init[0]); i++) {
        err |= LCD_WRITE_CMD(self, &init[i].cmd, 1);
        if (init[i].len != 0)
        {
            err |= LCD_WRITE_DATA(self, &init[i].data, init[i].len);
        }
    }

    return err;
}
